M2.883 · Aprendizaje por refuerzo

Máster universitario en Ciencia de datos (Data science)

Estudios de Informática, Multimedia y Telecomunicación

 

PEC2: Deep Reinforcement Learning

En esta práctica se implementarán tres modelos de DRL en un mismo entorno, con el objetivo de analizar distintas formas de aprendizaje de un agente y estudiar su rendimiento. El agente será entrenado con los métodos:

  1. DQN
  2. Double DQN (DDQN)
  3. REINFORCE con línea de base
  4. REINFORCE con reward modificado

Finalmente, crearemos un entorno de cero y lo resolveremos.

Importante: La entrega debe hacerse en formato notebook y en formato html donde se vea el código y los resultados y comentarios de cada ejercicio. Para exportar el notebook a html puede hacerse desde el menú File → Download as → HTML.

0. Contexto

Mountain Car consiste en un coche situado en un valle. El coche tiene muy poca potencia y sólo con su motor no es capaz de salir del valle porque la gravedad es más fuerte. Para ello, el agente debe conseguir impulsar el coche, ya sea acelerando hacia adelante o hacia atrás a la velocidad más adecuada para poder llegar a la cima de la montaña y salir del valle.

Las acciones que puede realizar el coche son las siguientes:

title

El agente recibirá una recompensa (penalización) de -1 cada vez que no consiga llegar a la posición objetivo.

Para más detalles sobre la definición del entorno de MountainCar, se recomienda consultar el código fuente: https://github.com/openai/gym/blob/master/gym/envs/classic_control/mountain_car.py

Cada episodio termina cuando el coche ha llegado al punto objetivo o si se han realizado 200 pasos. La solución óptima es aquella en la que el agente, con un desplazamiento eficiente, consigue llegar a la cima en el menor número de pasos posible.

1. Inicialización y exploración del entorno (0.5 ptos)

Empezaremos cargando las principales librerías necesarias para la práctica:

Ejercicio (0.1 ptos): Inicializar el entorno 'MountainCar-v0'. Extraer:
  • Valor del umbral de recompensa definido en el entorno
  • Máximo de pasos establecidos por cada episodio
  • Dimensión del espacio de acciones
  • Espacio de estados
  • Dimensión del espacio de estados
  • Valores mínimos permitidos de posición y velocidad
  • Valores máximos permitidos de posición y velocidad

Valor del umbral de recompensa definido en el entorno:

Máximo de pasos establecidos por cada episodio:

Dimensión del espacio de acciones:

Espacio de estados:

Dimensión del espacio de estados

Valor mínimo de posición y velocidad:

Valor máximo de posición y velocidad:

Ejercicio (0.2 ptos): Mostrar la representación vectorial de cada observación del entorno junto con la acción aleatoria seleccionada (ej. 'Observation: [..] , Action: Accelerate left'), en un intervalo de 10 episodios de 100 pasos cada uno. Opcional: visualizar los 10 episodios env.render(), sólo posible en local
Ejercicio (0.2 ptos): Ejecutar 1000 episodios con el máximo de pasos establecido en el entorno de MountainCar, tomando acciones de forma aleatoria. Almacenar la posición final del coche en cada episodio, y mostrar el resultado en un gráfico. Comentar el resultado.

Como se puede observar en el gráfico anterior las acciones que se toman son totalmente aleatorias, no siguen ningún patrón de mejora (lo cual es de esperar con la ejecución que hemos lanzado)

2. Agente DQN (2.5 ptos)

En este apartado implementaremos una DQN teniendo en cuenta la exploración-explotación (epsilon-greedy), la red objetivo, y el buffer de repetición de experiencias.

Definiremos el buffer como sigue:

Primeramente implementaremos la red neuronal, utilizando un modelo Secuencial con la siguiente configuración:

Usaremos el optimizador Adam para entrenar la red.

Ejercicio (0.75 ptos): Implementar la clase NeuralNet(). Inicializar las variables necesarias y definir el modelo Secuencial de red neuronal indicado. ----------------------------------------------------------------------------------------------------------- Nota: se os proporciona el código pre-implementado. La implementación que se pide en el enunciado está indicada en los bloques TODO y/o con variables igualadas a None.

A continuación implementaremos una clase que defina el comportamiento del agente DQN teniendo en cuenta:

Consideraremos que el agente ha aprendido a realizar la tarea (i.e. el "juego" termina) cuando obtiene una media de mínimo -110 puntos durante 100 episodios consecutivos.

Ejercicio (75 ptos): Implementar los siguientes puntos de la clase DQNAgent():
  1. Declarar las variables de la clase
  2. Inicializar las variables necesarias
  3. Implementar la acción a tomar
  4. Actualizar la red principal según la frecuencia establecida en los hiperparámetros
  5. Calcular la pérdida (ecuación Bellman, etc)
  6. Sincronizar la red objetivo según la frecuencia establecida en los hiperparámetros
  7. Calcular la media de recompensas de los últimos 100 episodios
  8. Comprobar límite de episodios
  9. Actualizar epsilon según: $$\textrm{max}(\epsilon · \epsilon_{\textrm{decay}}, 0.01)$$
Además, durante el proceso se deben almacenar (*):
  • Las recompensas obtenidas en cada paso del entrenamiento
  • Las recompensas medias cada 100 episodios
  • La pérdida durante el entrenamiento
  • La posición final del coche en cada episodio
  • La evolución de epsilon a lo largo del entrenamiento
----------------------------------------------------------------------------------------------------------- Nota: se os proporciona el código pre-implementado. La implementación que se pide en el enunciado está indicada en los bloques TODO y/o con variables igualadas a None, salvo (*) en qué momento almacenar las variables que se indican.

A continuación entrenaremos el modelo con los siguientes hiperparámetros:

Ejercicio (0.25 ptos): Declarar los hiperparámetros, cargar el modelo de red neuronal y entrenar el agente. Guardar el modelo entrenado en formato ".pth".
Ejercicio (0.25 ptos): Representar:
  1. Gráfico con las recompensas obtenidas a lo largo del entrenamieno, la evolución de las recompensas medias cada 100 episodios, y el umbral de recompensa establecido por el entorno.
  2. Gráfico con la posición final del coche en cada episodio
  3. Gráfico con la evolución de la perdida a lo largo del entrenamiento
  4. Gráfico con la evolución de epsilon a lo largo del entrenamiento
Comentar los resultados obtenidos.

Una vez entrenado el agente, nos interesa comprobar cómo de bien ha aprendido y si es capaz de conseguir que el coche llegue a su objetivo. Para ello, recuperamos el modelo entrenado y dejamos que el agente tome acciones aleatorias según ese modelo y observamos su comportamiento.

Ejercicio (0.5 ptos): Cargar el modelo entrenado y ejecutar el agente entrenado durante 100 episodios consecutivos. Almacenar la posición final del agente en cada episodio, y calcular la suma de recompensas por cada episodio. Mostrar:
  • un gráfico con la suma de las recompensas respecto de los episodios, incluyendo el umbral de recompensa establecido por el entorno
  • un gráfico con la posición final del coche en cada episodio
Comentar los resultados obtenidos. --------------------------------------------------------------------------------------------------- Opcional: visualizar los episodios con env.render(), sólo posible en local. Esta visualización ralentiza el proceso unos segundos por episodio.

Como se puede observar, en este caso el agente consigue llegar a la cima en los 100 episodios que se ejecutan, lo que indica que este ha aprendido correctamente.

3. Agente DDQN (2.5 ptos)

En este apartado implementaremos una doble DQN con la misma red neuronal y características de la DQN del apartado anterior.

Recordemos que con la doble DQN, es la red principal la que elije la acción con mayor valor de Q, y la red objetivo la que proporciona el valor objetivo de Q para esa acción, es decir, la que dirá qué recompensa tiene esa acción elegida por la red principal.

Siguiendo el mismo esquema que en el ejercicio anterior, a continuación implementaremos una clase que defina el entrenamiento del agente teniendo en cuenta:

Consideraremos que el agente ha aprendido a realizar la tarea (i.e. el "juego" termina) cuando obtiene una media de mínimo -110 puntos durante 100 episodios consecutivos.

Ejercicio (1 pto): Implementar los siguientes puntos de la clase DDQNAgent() siguiendo el mismo esquema de implementación realizado con el método DQNAgent()

A continuación entrenaremos el modelo con los mismos hiperparámetros que con la DQN.

Ejercicio (0.25 ptos): Entrenar el agente. Guardar el modelo entrenado en formato ".pth".
Ejercicio (0.25 ptos): Representar:
  1. Gráfico con las recompensas obtenidas a lo largo del entrenamieno, la evolución de las recompensas medias cada 100 episodios, y el umbral de recompensa establecido por el entorno
  2. Gráfico con la posición final del coche en cada episodio
  3. Gráfico con la evolución de la perdida a lo largo del entrenamiento
  4. Gráfico con la evolución de epsilon a lo largo del entrenamiento
Comentar los resultados obtenidos.

Una vez entrenado el agente, nos interesa comprobar cómo de bien ha aprendido, si el "robot" es capaz de realizar las tareas aprendidas. Para ello, recuperamos el modelo entrenado y dejamos que el agente tome acciones aleatorias según ese modelo y observamos su comportamiento.

Ejercicio (0.5 ptos): Cargar el modelo entrenado y ejecutar el agente entrenado durante 100 episodios consecutivos. Almacenar la posición final del agente en cada episodio, y calcular la suma de recompensas por cada episodio. Mostrar:
  • un gráfico con la suma de las recompensas respecto de los episodios, incluyendo el umbral de recompensa establecido por el entorno
  • un gráfico con la posición final del coche en cada episodio
Comentar los resultados obtenidos. --------------------------------------------------------------------------------------------------- Opcional: visualizar los episodios con env.render(), sólo posible en local. Esta visualización ralentiza el proceso unos segundos por episodio.

En este caso, los resultados obtenidos nos indican que haciendo uso del algoritmo DDQN entrenando al mismo con la configuración establecida, no consigue que el agente aprenda correctamente a llegar a la meta.

4. REINFORCE con línea de base (2 ptos)

En este apartado implementaremos un modelo basado en Gradientes de Política, el método de REINFORCE con línea de base.

Utilizaremos un modelo Secuencial con la siguiente configuración:

Usaremos el optimizador Adam para entrenar la red.

Ejercicio (0.75 ptos): Implementar la clase PGReinforce(). Inicializar las variables necesarias y definir el modelo Secuencial de red neuronal indicado. ----------------------------------------------------------------------------------------------------------- Nota: se os proporciona el código pre-implementado. La implementación que se pide en el enunciado está indicada en los bloques TODO y/o con variables igualadas a None.

A continuación definiremos el comportamiento del agente. En este caso no tendremos en cuenta el umbral de recompensa en la finalización del aprendizaje, sino dejaremos al agente realizar el máximo de episodios establecido.

Ejercicio (0.75 ptos): Implementar la clase reinforceAgent() teniendo en cuenta los siguientes puntos:
  1. Inicializar parámetros
  2. Inicializar las variables necesarias
  3. Implementar la acción a tomar
  4. Calcular el discounted rewards usando como línea de base la media del retorno estandarizada
  5. Calcular la media de recompensas de los últimos 100 episodios
  6. Actualizar red, almacenar pérdida y resetear variables cuando se completa el batch
  7. Implementar la pérdida por actualización
  8. Hacer las comprobaciones necesarias de fin de episodio
Además, durante el proceso se deben almacenar (*):
  • Las recompensas obtenidas en cada paso del entrenamiento
  • Las recompensas medias cada 100 episodios
  • La posición final del coche en cada episodio
  • La pérdida durante el entrenamiento
----------------------------------------------------------------------------------------------------------- Nota: se os proporciona el código pre-implementado. La implementación que se pide en el enunciado está indicada en los bloques TODO y/o con variables igualadas a None, salvo (*) en qué momento almacenar las variables que se indican.

Entrenamiento

A continuación entrenaremos el modelo con los siguientes hiperparámetros:

Ejercicio (0.25 ptos): Definir los hiperparámetros, cargar el modelo de red neuronal y entrenar el agente
Análisis (0.25 ptos): ¿Qué ha ocurrido? ¿El agente es capaz de aprender la tarea? ¿Por qué?

Lo que ha ocurrido es que el agente no es capaz de aprender la tarea y de hecho, las recompensas obtenidas tampoco bajan con el paso de los episodios. Esto se debe a que los gradientes de política, se basan en las recompensas obtenidas por el entorno para dar mayor o menor probabilidad a las acciones a tomar y crear de este modo la política óptima, es decir, de todas las acciones posibles a tomar en cada estado, a medida que el algoritmo avance, dará un menor peso o probabilidad a aquellas que proporcionan una recompensa menor y un mayor peso o probabilidad a aquellas que proporcionan una recompensa mayor. Utilizando el entorno actual tal y como está configurado, resulta muy complicado crear una política óptima basándose en las recompensas obtenidas por el mismo en cada estado, porque todos los movimientos dan la misma recompensa (-1), menos el estado donde se encuentra la meta (1), por lo que resulta muy difícil para el agente aprender que acciones debe tomar para acercarse al estado objetivo utilizando gradientes de política, ya que a diferencia de la función valor de los algoritmos anteriores, este solo se basa en las recompensa de cada estado, no tiene en cuenta lo que puede ocurrir en estados futuros.

4.1 Modificación de la recompensa

Como hemos visto, el entorno de MountainCar únicamente proporciona recompensa cuando el agente llega al objetivo. Pero no hay otras indicaciones que permitan al agente saber que está yendo en la buena dirección de aprendizaje, lo que hace el aprendizaje un poco aleatorio y difícil para el agente.

En este apartado vamos a modificar la recompensa del agente REINFORCE creando una función que dé al agente una recompensa según la posición en la que se encuentra. Que aunque no llegue al objetivo final pueda al menos saber que no iba tan mal encaminado.

Ejercicio (0.25 ptos): Definir una función get_reward() en la que, si el agente se encuentra:
  • en una posición mayor o igual a 0.5, la recompensa será: +10
  • en una posición mayor que -0.4, la recompensa será: $(1+s)^2$ , es decir, proporcional a la posición
  • en cualquier otra posición, se le penalizará con una recompensa de: -1
Ejercicio (0.5 ptos): Modificar la clase reinforceAgent del ejercicio anterior para que el agente reciba las nuevas recompensas. Entrenar el agente con los mismos parámetros que en el ejercicio anterior, y mostrar las gráficas de recompensas y de la posición final del coche en cada episodio.
Análisis (0.2 ptos): Comentar los resultados obtenidos. ¿Ha conseguido el agente REINFORCE aprender a llegar al objetivo? Indicar qué se podría hacer para mejorar el resultado.

En este caso, el agente REINFORCE sí que ha aprendido llegar al objetivo y de hecho de forma más eficiente que utilizando algoritmos de redes profundas Q (DQN). En este caso, el agente ha podido aprender a llegar al objetivo debido a que las recompensas que tenían los estados más cercanos al mismo eran mayores, lo que permite al agente REINFORCE poder crear una política óptima basándose en las recompensas obtenidas.

Para mejorar el resultado, quizá se le podría haber dado un mayor peso también a aquellas posiciones que estaban más cercanos al objetivo aún siendo menores que -0,4.

5. Comparación de modelos (0.5 ptos)

Ejercicio: Mostrar en un mismo gráfico la evolución de la media de recompensas de los modelos DQN y DDQN, junto con el umbral de recompensa. Comentar los resultados.

En este caso, se puede observar como el algoritmo DQN aprende de una forma más rápida que el algoritmo DDQN. Mientras el algoritmo DDQN va mejorando muy poco a poco, el algoritmo DQN va mejorando a partir del episodio 1000 muy rápidamente.

6. Creación de un entorno en Gym (2 ptos)

En este ejercicio diseñaremos un entorno sencillo siguiendo el esquema de los entornos de gym, y trataremos de resolverlo.

Los entornos de gym suelen tener la siguiente estructura:

class FooEnv(gym.Env):
  metadata = {'render.modes': ['human']}

  def __init__(self):
    ...
  def step(self, action):
    ...
  def reset(self):
    ...
  def render(self, mode='human', close=False):
    ...

Para nuestro entorno usaremos todas estas funciones salvo el render (relativo al diseño de la visualización el entorno).

Primeramente importamos la librerías necesarias:

6.1 Entorno

El entorno que vamos a diseñar consiste en un regulador de temperatura. Imaginemos que queremos tener una ducha inteligente que sea capaz de mantener la temperatura en un intervalo concreto (37-39 grados) durante todo el tiempo de baño que fijemos. Cada acción será subir o bajar la temperatura un grado, o mantener la temperatura actual.

Concretamente queremos que el regulador de temperatura tenga las siguientes características:

Ejercicio (0.75 ptos): Diseñar el entorno TempControlEnv con las características indicadas.
Ejercicio (0.25 ptos): Cargar el entorno TempControlEnv y mostrar el espacio de acciones y el espacio de observaciones. Ejecutar 10 episodios con acciones aleatorias, mostrando el episodio y la recompensa obtenida.

6.2 Agente regulador de la temperatura

Ejercicio (1 pto): Implementar un agente capaz de regular la temperatura y mantenerla en los intervalos establecidos por el entorno TempControlEnv diseñado. Se puede implementar cualquier tipo de agente visto en el curso (módulos 9, 10 y 11) con los hiperparámetros que se considere conveniente.
  • Mostrar gráficamente la evolución de las recompensas
  • Mostrar gráficamente la evolución de la temperatura final de cada episodio a lo largo del entrenamiento
  • Testear el modelo de agente con 100 episodios y mostrar las gráficas de recompensas y temperatura